<?php

namespace App\Kuafu\WaiMai\Service;

use App\Kuafu\Mqtt\Enum\MqttCodeEnum;
use App\Kuafu\Mqtt\Service\MqttService;
use App\Kuafu\Shop\Model\Shop;
use App\Kuafu\WaiMai\Channel\BaseWaiMai;
use App\Kuafu\WaiMai\Channel\ElmWaiMai;
use App\Kuafu\WaiMai\Enum\WaiMaiCodeEnum;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use App\Kuafu\WaiMai\Exceptions\WaiMaiException;
use App\Kuafu\WaiMai\Jobs\WaiMaiDelayTask;
use App\Kuafu\WaiMai\Model\WaiMaiOrderModel;
use App\Kuafu\WaiMai\Model\WaiMaiShopFinishConfigModel;
use App\Kuafu\WaiMai\Model\WaiMaiShopFinishSwitchModel;
use App\Kuafu\WaiMai\Model\WaiMaiShopPromiseSwitchModel;
use App\Kuafu\WaiMai\Model\WaiMaiShopTimeConfigModel;
use App\Kuafu\WaiMai\Validate\WaiMaiValidate;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class WaiMaiFinishFoodService
{
    private int $shopId;

    /**
     * @param int $shop
     */
    public function __construct(int $shopId)
    {
        $this->shopId = $shopId;
    }

    /**
     * 获取完整配置信息
     * @param bool $closeFillLists 当自动出餐关闭时，是否查表显示列表信息
     *
     * @return array
     */
    public function getConfig(bool $closeFillLists = true): array
    {
        // 店铺开关
        $finishSwitch = $this->getConfigSwitch();
        $configSwitch = $finishSwitch['switch'];

        $lists = [];

        // 查表的情况
        if ($configSwitch) {
            $lists = $this->getTimeFinishConfigDB();
        }
        if (!$configSwitch && $closeFillLists) {
            $lists = $this->getTimeFinishConfigDB();
        }

        // 给默认值
        if (!$lists) {
            $lists = $this->getTimeFinishConfigDefault();
        }

        return [
            'switch'         => $configSwitch,
            'lists'          => $lists,
            'reservePrepare' => $finishSwitch['reservePrepare'],
            'reserveFinish'  => $finishSwitch['reserveFinish'],
        ];
    }

    /**
     * 设置完整配置
     * @param int $switch
     * @param array $lists
     * @param int $reservePrepare
     * @param int $reserveFinish
     * @throws WaiMaiException
     */
    public function setConfig(int $switch, array $lists, int $reservePrepare, int $reserveFinish): void
    {
        // 校验格式
        WaiMaiValidate::finishSwitch([
            'switch'         => $switch,
            'reservePrepare' => $reservePrepare,
            'reserveFinish'  => $reserveFinish
        ]);
        foreach ($lists as $time) {
            WaiMaiValidate::timeConfig($time);

            $finishConfig = $time['finishConfig'] ?? [];
            foreach ($finishConfig as $finish) {
                WaiMaiValidate::finishConfig($finish);
            }
        }

        try {
            // 操作DB
            DB::connection('mysqlWaiMaiAll')
              ->beginTransaction();

            WaiMaiShopTimeConfigModel::delData($this->shopId);
            WaiMaiShopFinishConfigModel::delData($this->shopId);

            foreach ($lists as $time) {
                $timeModel = WaiMaiShopTimeConfigModel::addData($this->shopId, $time['timeType'], $time['timeStart'],
                    $time['timeEnd']);

                foreach ($time['finishConfig'] as $finish) {
                    WaiMaiShopFinishConfigModel::addData($this->shopId, $timeModel->id, $finish['priceType'],
                        $finish['seconds']);
                }
            }

            // 店铺主表配置
            WaiMaiShopFinishSwitchModel::addData($this->shopId, $switch, $reservePrepare, $reserveFinish);

            DB::connection('mysqlWaiMaiAll')
              ->commit();

        } catch (\Exception $e) {
            DB::connection('mysqlWaiMaiAll')
              ->rollBack();
            Log::warning($e);

            throw new WaiMaiException(WaiMaiCodeEnum::ERR_SET_CONFIG_DB);
        }
    }

    /**
     * 获取外卖开关主表信息
     */
    public function getConfigSwitch(): array
    {
        $model = WaiMaiShopFinishSwitchModel::query()
                                            ->where('shopId', $this->shopId)
                                            ->select('switch', 'reservePrepare', 'reserveFinish')
                                            ->first();
        if ($model) {
            return $model->toArray();
        }

        // 默认配置
        return [
            'switch'         => 0,
            'reservePrepare' => 3600,
            'reserveFinish'  => 1800,
        ];
    }

    /**
     * 从DB获取外卖自动出餐的详细配置
     * @return array
     */
    private function getTimeFinishConfigDB(): array
    {
        return WaiMaiShopTimeConfigModel::query()
                                        ->with('finishConfigRel')
                                        ->where('shopId', $this->shopId)
                                        ->get()
                                        ->map(function ($item) {
                                            return [
                                                'timeType'     => $item->timeType,
                                                'timeTypeText' => $item->timeTypeText,
                                                'timeStart'    => $item->timeStart,
                                                'timeEnd'      => $item->timeEnd,
                                                'finishConfig' => $item->finishConfigRel
                                                    ->map(function ($relItem) {
                                                        return [
                                                            'priceType'     => $relItem->priceType,
                                                            'priceTypeText' => $relItem->priceTypeText,
                                                            'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[$relItem->priceType],
                                                            'seconds'       => $relItem->seconds,
                                                        ];
                                                    })
                                                    ->toArray(),
                                            ];
                                        })
                                        ->toArray();
    }

    /**
     * 获取外卖自动出餐的详细配置默认值
     * @return array
     */
    private function getTimeFinishConfigDefault(): array
    {
        return [
            [
                'timeType'     => WaiMaiShopTimeConfigModel::TIME_TYPE_1,
                'timeTypeText' => WaiMaiShopTimeConfigModel::TIME_TYPE_TEXT[WaiMaiShopTimeConfigModel::TIME_TYPE_1],
                'timeStart'    => '00:00:00',
                'timeEnd'      => '00:00:00',
                'finishConfig' => [
                    [
                        'priceType'     => WaiMaiShopFinishConfigModel::PRICE_TYPE_1,
                        'priceTypeText' => WaiMaiShopFinishConfigModel::getPriceTypeText(WaiMaiShopFinishConfigModel::PRICE_TYPE_1),
                        'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[WaiMaiShopFinishConfigModel::PRICE_TYPE_1],
                        'seconds'       => 180,
                    ],
                    [
                        'priceType'     => WaiMaiShopFinishConfigModel::PRICE_TYPE_2,
                        'priceTypeText' => WaiMaiShopFinishConfigModel::getPriceTypeText(WaiMaiShopFinishConfigModel::PRICE_TYPE_2),
                        'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[WaiMaiShopFinishConfigModel::PRICE_TYPE_2],
                        'seconds'       => 300,
                    ],
                    [
                        'priceType'     => WaiMaiShopFinishConfigModel::PRICE_TYPE_3,
                        'priceTypeText' => WaiMaiShopFinishConfigModel::getPriceTypeText(WaiMaiShopFinishConfigModel::PRICE_TYPE_3),
                        'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[WaiMaiShopFinishConfigModel::PRICE_TYPE_3],
                        'seconds'       => 480,
                    ],
                ],
            ],
            [
                'timeType'     => WaiMaiShopTimeConfigModel::TIME_TYPE_2,
                'timeTypeText' => WaiMaiShopTimeConfigModel::TIME_TYPE_TEXT[WaiMaiShopTimeConfigModel::TIME_TYPE_2],
                'timeStart'    => '11:20:00',
                'timeEnd'      => '12:30:00',
                'finishConfig' => [
                    [
                        'priceType'     => WaiMaiShopFinishConfigModel::PRICE_TYPE_1,
                        'priceTypeText' => WaiMaiShopFinishConfigModel::getPriceTypeText(WaiMaiShopFinishConfigModel::PRICE_TYPE_1),
                        'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[WaiMaiShopFinishConfigModel::PRICE_TYPE_1],
                        'seconds'       => 600,
                    ],
                    [
                        'priceType'     => WaiMaiShopFinishConfigModel::PRICE_TYPE_2,
                        'priceTypeText' => WaiMaiShopFinishConfigModel::getPriceTypeText(WaiMaiShopFinishConfigModel::PRICE_TYPE_2),
                        'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[WaiMaiShopFinishConfigModel::PRICE_TYPE_2],
                        'seconds'       => 720,
                    ],
                    [
                        'priceType'     => WaiMaiShopFinishConfigModel::PRICE_TYPE_3,
                        'priceTypeText' => WaiMaiShopFinishConfigModel::getPriceTypeText(WaiMaiShopFinishConfigModel::PRICE_TYPE_3),
                        'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[WaiMaiShopFinishConfigModel::PRICE_TYPE_3],
                        'seconds'       => 720,
                    ],
                ],
            ],
            [
                'timeType'     => WaiMaiShopTimeConfigModel::TIME_TYPE_3,
                'timeTypeText' => WaiMaiShopTimeConfigModel::TIME_TYPE_TEXT[WaiMaiShopTimeConfigModel::TIME_TYPE_3],
                'timeStart'    => '17:30:00',
                'timeEnd'      => '18:40:00',
                'finishConfig' => [
                    [
                        'priceType'     => WaiMaiShopFinishConfigModel::PRICE_TYPE_1,
                        'priceTypeText' => WaiMaiShopFinishConfigModel::getPriceTypeText(WaiMaiShopFinishConfigModel::PRICE_TYPE_1),
                        'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[WaiMaiShopFinishConfigModel::PRICE_TYPE_1],
                        'seconds'       => 600,
                    ],
                    [
                        'priceType'     => WaiMaiShopFinishConfigModel::PRICE_TYPE_2,
                        'priceTypeText' => WaiMaiShopFinishConfigModel::getPriceTypeText(WaiMaiShopFinishConfigModel::PRICE_TYPE_2),
                        'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[WaiMaiShopFinishConfigModel::PRICE_TYPE_2],
                        'seconds'       => 720,
                    ],
                    [
                        'priceType'     => WaiMaiShopFinishConfigModel::PRICE_TYPE_3,
                        'priceTypeText' => WaiMaiShopFinishConfigModel::getPriceTypeText(WaiMaiShopFinishConfigModel::PRICE_TYPE_3),
                        'priceInterval' => WaiMaiShopFinishConfigModel::PRICE_TYPE_NUM[WaiMaiShopFinishConfigModel::PRICE_TYPE_3],
                        'seconds'       => 720,
                    ],
                ],
            ]
        ];
    }

    /**
     * 接单后自动生成出餐队列
     * @param int $orderId
     * @param int $price
     * @param int $expectFast
     * @param int $expectTime
     */
    public function acceptAutoDelayFinish(int $orderId, int $price, int $expectFast, int $expectTime, int $silentSwitch): self
    {
        if ($expectFast == 1) {

            // 即时单 - 按门店配置计算延迟时间
            $delaySeconds = $this->getSeconds($price);
        } else {

            // 预约单提前出餐时间
            $reserveFinish = $this->getConfigSwitch()['reserveFinish'];

            // 按配置计算预约单出餐时间
            $delaySeconds = $expectTime - time() - $reserveFinish;
            if ($delaySeconds < 0) {

                // 来不及了，按即时单处理
                $delaySeconds = $this->getSeconds($price);;
            }
        }

        // 扔进队列
        WaiMaiDelayTask::dispatch(WaiMaiDelayTask::TASK_TYPE_3, $orderId, ['silentSwitch' => $silentSwitch])
                       ->onQueue(WaiMaiDelayTask::QUEUE_NAME)
                       ->delay($delaySeconds);

        return $this;
    }

    /**
     * 兜底的强制自动出餐
     * @param int $orderId
     * @param int $delaySeconds
     * @param int $silentSwitch
     * @return $this
     */
    public function lastForceAutoFinish(int $orderId, int $delaySeconds, int $silentSwitch): self
    {
        // 扔进队列
        WaiMaiDelayTask::dispatch(WaiMaiDelayTask::TASK_TYPE_5, $orderId, ['silentSwitch' => $silentSwitch])
                       ->onQueue(WaiMaiDelayTask::QUEUE_NAME)
                       ->delay($delaySeconds);

        return $this;
    }

    /**
     * 接单后自动生成 预约单备餐队列
     * @param int $orderId
     * @param int $expectFast
     * @param int $expectTime
     */
    public function acceptAutoDelayPrepare(int $orderId, int $expectFast, int $expectTime): self
    {
        if ($expectFast) {
            return $this;
        }

        // 预约单备餐时间
        $reservePrepare = $this->getConfigSwitch()['reservePrepare'];

        // 按配置计算预约单备餐时间
        $delaySeconds = $expectTime - time() - $reservePrepare;
        if ($delaySeconds < 0) {

            // 来不及了，即时队列
            WaiMaiDelayTask::dispatch(WaiMaiDelayTask::TASK_TYPE_4, $orderId, [])
                           ->onQueue(WaiMaiDelayTask::QUEUE_NAME);
            return $this;
        }

        // 延迟队列
        WaiMaiDelayTask::dispatch(WaiMaiDelayTask::TASK_TYPE_4, $orderId, [])
                       ->onQueue(WaiMaiDelayTask::QUEUE_NAME)
                       ->delay($delaySeconds);

        return $this;
    }

    /**
     *  获取出餐时长
     */
    public function getSeconds(int $price, int $timestamp = 0): int
    {
        $config = $this->getConfig(false);

        // 关闭自动出餐
        if (!$config['switch']) {
            return -1;
        }

        // 判断时间段
        if (!$timestamp) {
            $timestamp = time();
        }
        $timeStampHis = date('H:i:s', $timestamp);

        $finishConfig      = [];
        $spareFinishConfig = [];
        foreach ($config['lists'] as $val) {

            // 闲时不处理
            if ($val['timeType'] == WaiMaiShopTimeConfigModel::TIME_TYPE_1) {
                $spareFinishConfig = $val['finishConfig'];
                continue;
            }

            if ($timeStampHis >= $val['timeStart'] && $timeStampHis < $val['timeEnd']) {
                $finishConfig = $val['finishConfig'];
                break;
            }
        }

        // 没匹配到时间段，就直接给闲时的
        if (!$finishConfig) {
            $finishConfig = $spareFinishConfig;
        }

        // 判断金额
        $seconds = 0;
        foreach ($finishConfig as $val) {
            if ($price >= $val['priceInterval'][0] && $price < $val['priceInterval'][1]) {
                $seconds = $val['seconds'];
                break;
            }
        }

        return $seconds;
    }

    /**
     * 手动设置出餐弹框延迟推送
     * @param int $orderId
     * @param int $seconds
     */
    public function delayPush(int $orderId, int $seconds): void
    {
        WaiMaiValidate::delayPush(['orderId' => $orderId, 'seconds' => $seconds]);

        $order = WaiMaiOrderModel::suffix($this->shopId)->where('orderId', $orderId)->select('orderStatus')->first();
        if (!$order) {
            throw new WaiMaiException(WaiMaiCodeEnum::ERR_EMPTY_ORDER_ID);
        }

        if ($order->orderStatus >= WaiMaiDBEnum::ORDER_STATUS_4) {
            throw new WaiMaiException(WaiMaiCodeEnum::ERR_DO_FINISH_FOODS, '订单已被骑手取走');
        }

        // 扔进队列
        WaiMaiDelayTask::dispatch(WaiMaiDelayTask::TASK_TYPE_3, $orderId, [])
                       ->onQueue(WaiMaiDelayTask::QUEUE_NAME)
                       ->delay($seconds);
    }

    /**
     * 触发出餐延迟推送
     * @param int $orderId
     */
    public function doFinishPush(int $orderId, int $silentSwitch): void
    {
        $order = WaiMaiOrderModel::suffix($this->shopId)->where('orderId', $orderId)->select('orderStatus', 'sourceType', 'isFinishFoods')->first();
        if (!$order) {
            throw new WaiMaiException(WaiMaiCodeEnum::ERR_EMPTY_ORDER_ID);
        }

        if ($order->orderStatus >= WaiMaiDBEnum::ORDER_STATUS_4) {
            throw new WaiMaiException(WaiMaiCodeEnum::ERR_DO_FINISH_FOODS, '订单已被骑手取走');
        }

        if ($order->isFinishFoods == WaiMaiDBEnum::FINISH_OK) {
            throw new WaiMaiException(WaiMaiCodeEnum::ERR_DO_FINISH_FOODS, "该订单已出餐，请勿重复出餐");
        }

        // 推送MQTT
        WaiMaiMqttService::getIns($this->shopId)
                         ->takeOutOrderOperate($order->sourceType, $orderId, MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_8, ['silentSwitch' => $silentSwitch]);
    }

    /**
     * 触发预约单备餐提醒推送
     * @param int $orderId
     * @throws WaiMaiException
     */
    public function doPreparePush(int $orderId): void
    {
        $order = WaiMaiOrderModel::suffix($this->shopId)->where('orderId', $orderId)->select('orderStatus', 'sourceType')->first();
        if (!$order) {
            throw new WaiMaiException(WaiMaiCodeEnum::ERR_EMPTY_ORDER_ID);
        }

        if ($order->orderStatus >= WaiMaiDBEnum::ORDER_STATUS_4) {
            throw new WaiMaiException(WaiMaiCodeEnum::ERR_DO_FINISH_FOODS, '订单已被骑手取走');
        }

        if ($order->isFinishFoods == WaiMaiDBEnum::FINISH_OK) {
            throw new WaiMaiException(WaiMaiCodeEnum::ERR_DO_FINISH_FOODS, '订单已出餐');
        }

        // 推送MQTT
        WaiMaiMqttService::getIns($this->shopId)
                         ->takeOutOrderOperate($order->sourceType, $orderId, MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_9);
    }

    /**
     * 触发兜底的强制自动出餐
     * @param int $orderId
     * @param int $silentSwitch
     * @return void
     */
    public function doLastForceFinishPush(int $orderId, int $silentSwitch): void
    {
        // 出餐
        $doFinish = WaiMaiOrderService::sFinishFoods($orderId, $this->shopId);

        // 出餐成功
        if ($doFinish) {

            $order = WaiMaiOrderModel::suffix($this->shopId)
                                     ->where('orderId', $orderId)
                                     ->select('orderStatus', 'sourceType')
                                     ->first();

            Log::info("doLastForceFinishPush success orderId: {$orderId}");

            // 推送MQTT
            WaiMaiMqttService::getIns($this->shopId)
                             ->takeOutOrderOperate($order->sourceType, $orderId, MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_10, ['silentSwitch' => $silentSwitch]);
        } else {

            // 出餐失败
            Log::info("doLastForceFinishPush error orderId: {$orderId}");
        }
    }

    /**
     * 获取承诺出餐配置
     */
    public function getPromiseFinishConfig(): array
    {
        $promiseConfig = WaiMaiShopPromiseSwitchModel::query()->where('shopId', $this->shopId)->first();
        if (!$promiseConfig) {
            $promiseConfig = (new WaiMaiShopPromiseSwitchModel)->addData($this->shopId);
        }

        return [
            'config'  => $promiseConfig->setHidden(['id', 'createdAt', 'updatedAt', 'createdByUid', 'updatedByUid'])
                                       ->toArray(),
            'default' => [
                'kfPromiseTime'    => WaiMaiDBEnum::KF_PROMISE_DEFAULT_TIME,
                'elmPromiseSwitch' => WaiMaiDBEnum::ELM_PROMISE_DEFAULT_SWITCH,
                'silentSwitch'     => WaiMaiDBEnum::SILENT_DEFAULT_SWITCH,
            ],
        ];
    }

    /**
     * 设置承诺出餐配置
     * @param int $kfPromiseTime
     * @param int $elmPromiseSwitch
     * @param int $silentSwitch
     */
    public function setPromiseFinishConfig(int $kfPromiseTime, int $elmPromiseSwitch, int $silentSwitch)
    {

        $elmPromiseTime = 0;
        if ($elmPromiseSwitch == WaiMaiDBEnum::SWITCH_ON) {

            // 拉取最新饿了么时间
            $elmPromiseTime = BaseWaiMai::getIns(WaiMaiDBEnum::SOURCE_TYPE_ELM, $this->shopId)
                                        ->finishTimeConfig()['data']['promiseTime'] ?? 0;
        }

        return (new WaiMaiShopPromiseSwitchModel([
            'kfPromiseTime'    => $kfPromiseTime,
            'elmPromiseSwitch' => $elmPromiseSwitch,
            'silentSwitch'     => $silentSwitch,
            'elmPromiseTime'   => $elmPromiseTime,
        ]))->addData($this->shopId);
    }

}
